package com.kun.video.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.kun.video.context.annotation.Component;
import com.kun.video.context.annotation.PostConstruct;
import com.kun.video.exception.BizException;
import com.kun.video.thread.ComThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Consumer;

/**
 * 高性能命令行执行工具类（FFmpeg专用）- 深度优化版
 * <p>
 * 深度优化特性：
 * 1. 分级线程池管理（执行与IO分离）：使用独立线程池处理进程执行和输出流读取，避免IO阻塞
 * 2. 双阶段超时控制：先尝试优雅终止（允许进程清理资源），超时后强制终止进程树
 * 3. 流式输出处理：采用有界队列缓冲输出，避免内存溢出，同时保证实时性
 * 4. 增强型进程树终止：确保彻底终止所有子进程，防止僵尸进程
 * 5. 异常上下文保留：完整保留执行上下文信息，便于问题排查
 * <p>
 * 设计要点：
 * - 最大输出限制：默认保留最后1000行输出，平衡内存使用与调试需求
 * - 安全隔离：通过独立线程池隔离可能阻塞的操作
 * - 资源泄漏防护：通过finally块确保进程资源释放
 */
@Component
public class CommandExecutor {
    private static final Logger log = LoggerFactory.getLogger(CommandExecutor.class);

    /**
     * 优雅终止等待时间（单位：秒）
     */
    private static final int GRACEFUL_SHUTDOWN_TIMEOUT = 5;
    /**
     * 强制终止等待时间（单位：秒）
     */
    private static final int FORCE_SHUTDOWN_TIMEOUT = 5;
    /**
     * 最大保留输出行数
     */
    private static final int MAX_OUTPUT_LINES = 1000;
    /**
     * 线程池组件（执行与IO分离）
     */
    private static ComThreadPool comThreadPool;

    /**
     * 初始化
     *
     * @param comThreadPool 通用线程池
     */
    @PostConstruct
    public void init(ComThreadPool comThreadPool) {
        CommandExecutor.comThreadPool = comThreadPool;
    }

    public static CommandResult executeCommand(String... commandParts) {
        return executeCommand(Arrays.asList(commandParts), 30, null);
    }

    public static CommandResult executeCommand(String command) {
        return executeCommand(parseCommandString(command), 30, null);
    }

    /**
     * 新增方法：支持命令行字符串输入（自动处理参数分割）
     * 使用改进的split算法处理带空格的参数（支持双引号包裹）
     */
    public static CommandResult executeCommand(String command, int timeout) {
        return executeCommand(parseCommandString(command), timeout, null);
    }

    /**
     * 带异常抛出的严格版本（适用于必须成功的场景）
     */
    public static CommandResult executeCommandStrict(String command, int timeout) {
        CommandResult result = executeCommand(command, timeout);
        if (!result.isSuccess()) {
            throw new BizException("命令执行失败: " + result.getErrorTrace());
        }
        return result;
    }

    public static CommandResult executeCommand(String command, Consumer<String> outputLineHandler) {
        return executeCommand(parseCommandString(command), 60000, outputLineHandler);
    }

    public static CommandResult executeCommand(List<String> commandParts, Consumer<String> outputLineHandler) {
        return executeCommand(commandParts, 60000, outputLineHandler);
    }

    /**
     * 执行命令行命令（线程安全）
     *
     * @param commandParts 命令参数列表（必须包含有效可执行路径）
     * @param timeout      总超时时间（秒），建议 >= 5
     * @return 包含完整执行上下文的结果对象
     * @throws IllegalArgumentException 当命令参数非法时抛出
     */
    public static CommandResult executeCommand(List<String> commandParts, int timeout, Consumer<String> outputLineHandler) {
        log.info("执行命令 -> {}", StrUtil.join(" ", commandParts));
        validateCommand(commandParts);
        Process process = null;
        try {
            process = startProcess(commandParts);
            return monitorProcess(process, timeout, outputLineHandler);
        } catch (Exception e) {
            return new CommandResult(-1, Collections.emptyList(), e);
        } finally {
            // 确保无论是否异常都尝试终止进程
            if (process != null && process.isAlive()) {
                terminateProcessTree(process);
            }
        }
    }

    /**
     * 启动新进程并配置流重定向
     *
     * @param command 有效命令参数列表
     * @return 已启动的进程对象
     * @throws BizException 当进程启动失败时抛出（包含原始异常信息）
     */
    private static Process startProcess(List<String> command) {
        try {
            // 合并标准错误与标准输出
            return new ProcessBuilder(command)
                    .redirectErrorStream(true)
                    .start();
        } catch (IOException | SecurityException e) {
            throw new BizException("进程启动失败: " + String.join(" ", command), e);
        }
    }


    /**
     * 监控进程执行（含双阶段超时控制）
     *
     * @param process 目标进程
     * @param timeout 总超时时间（秒）
     * @return 执行结果（可能包含部分输出）
     */
    private static CommandResult monitorProcess(Process process, int timeout, Consumer<String> outputLineHandler) {
        ProcessMonitor monitor = new ProcessMonitor(process, outputLineHandler);
        Future<CommandResult> future = comThreadPool.submit(monitor);

        try {
            return future.get(timeout, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            log.warn("命令执行超时（{}秒），尝试优雅终止", timeout);
            return handleTimeout(process, monitor);
        } catch (ExecutionException | InterruptedException e) {
            Thread.currentThread().interrupt();  // 保持中断状态
            return new CommandResult(-3, Collections.emptyList(), e);
        }
    }

    /**
     * 处理超时场景（优雅终止 -> 强制终止）
     *
     * @return 包含已捕获输出的结果对象
     */
    private static CommandResult handleTimeout(Process process, ProcessMonitor monitor) {
        try {
            comThreadPool.submit(() -> gracefulShutdown(process, monitor))
                    .get(FORCE_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            log.error("强制终止超时，可能存在僵尸进程 PID:{}", process.pid());
        } catch (Exception e) {
            log.warn("终止操作异常", e);
        }
        return monitor.getPartialResult();
    }

    /**
     * 优雅终止流程（允许进程清理资源）
     */
    private static void gracefulShutdown(Process process, ProcessMonitor monitor) {
        try {
            if (!monitor.awaitTermination(GRACEFUL_SHUTDOWN_TIMEOUT)) {
                log.debug("优雅终止失败，强制终止进程树 PID:{}", process.pid());
                terminateProcessTree(process);
            }
        } catch (InterruptedException e) {
            throw new BizException("线程中止发送异常", e);
        }
    }

    /**
     * 彻底终止进程树（支持跨平台）
     *
     * @param process 目标根进程
     */
    private static void terminateProcessTree(Process process) {
        process.descendants().forEach(ph -> {
            ph.destroyForcibly();
            log.debug("终止子进程 PID:{}", ph.pid());
        });
        if (process.isAlive()) {
            process.destroyForcibly();
            log.debug("终止主进程 PID:{}", process.pid());
        }
    }

    /**
     * 验证命令参数有效性
     *
     * @throws IllegalArgumentException 当参数非法时抛出
     */
    private static void validateCommand(List<String> commandParts) {
        if (CollUtil.isEmpty(commandParts)) {
            throw new IllegalArgumentException("命令参数不能为null或空");
        }
        if (commandParts.stream().anyMatch(str -> str == null || str.isBlank())) {
            throw new IllegalArgumentException("命令包含无效空参数: " + commandParts);
        }
    }

    /**
     * 安全解析命令行字符串（支持带空格的引号参数）
     * 示例："ffmpeg -i \"input file.mp4\" output"
     * 解析为：["ffmpeg", "-i", "input file.mp4", "output"]
     *
     * @param command 原始命令字符串
     * @return 安全分割后的参数列表
     */
    private static List<String> parseCommandString(String command) {
        if (StrUtil.isBlank(command)) {
            throw new IllegalArgumentException("命令字符串不能为空");
        }

        List<String> parts = new ArrayList<>();
        StringBuilder buffer = new StringBuilder();
        boolean inQuotes = false;
        char prevChar = 0;

        for (char c : command.toCharArray()) {
            if (c == '"' && prevChar != '\\') {
                inQuotes = !inQuotes;
                continue;
            }

            if (c == ' ' && !inQuotes) {
                if (buffer.length() > 0) {
                    parts.add(buffer.toString());
                    buffer.setLength(0);
                }
            } else {
                buffer.append(c);
            }

            prevChar = c;
        }

        if (buffer.length() > 0) {
            parts.add(buffer.toString());
        }

        if (inQuotes) {
            throw new IllegalArgumentException("未闭合的引号: " + command);
        }

        return parts;
    }


    /**
     * 进程监控封装类（非静态内部类以持有外部状态）
     */
    private static class ProcessMonitor implements Callable<CommandResult> {
        private final Process process;
        /**
         * 有界队列平衡内存与实时性（容量=MAX_OUTPUT_LINES）
         */
        private final BlockingQueue<String> outputQueue = new LinkedBlockingQueue<>(MAX_OUTPUT_LINES);
        /**
         * 输出流读取异常
         */
        private volatile Throwable error;
        /**
         * 进程输出行处理
         */
        private final Consumer<String> outputLineHandler;

        ProcessMonitor(Process process, Consumer<String> outputLineHandler) {
            this.process = process;
            this.outputLineHandler = outputLineHandler;
            // 立即启动输出消费者
            startOutputConsumer();
        }

        /**
         * 启动独立线程消费进程输出（防止流阻塞）
         */
        private void startOutputConsumer() {
            comThreadPool.submit(() -> {
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        // 非阻塞式写入（保留最新输出）
                        if (!outputQueue.offer(line)) {
                            // 移除最旧条目
                            outputQueue.poll();
                            outputQueue.add(line);
                        }
                        if (outputLineHandler != null) {
                            outputLineHandler.accept(line);
                        }
                    }
                } catch (IOException e) {
                    error = e;
                } finally {
                    // 清理中断状态
                    Thread.currentThread().interrupt();
                }
            });
        }

        @Override
        public CommandResult call() throws Exception {
            try {
                // 阻塞直到进程结束
                int exitCode = process.waitFor();
                return new CommandResult(exitCode, drainOutput(), null);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new BizException("进程监控被中断", e);
            } finally {
                // 确保资源释放
                terminateProcessTree(process);
            }
        }

        /**
         * 获取已捕获的输出（线程安全）
         */
        CommandResult getPartialResult() {
            return new CommandResult(-2, drainOutput(), error);
        }

        /**
         * 等待进程终止（带超时）
         *
         * @return 是否在超时前终止
         */
        boolean awaitTermination(int timeout) throws InterruptedException {
            return process.waitFor(timeout, TimeUnit.SECONDS);
        }

        /**
         * 排空输出队列（限制最大行数）
         */
        private List<String> drainOutput() {
            return outputQueue.stream().limit(MAX_OUTPUT_LINES).toList();
        }
    }

    /**
     * 增强型执行结果（不可变对象）
     *
     * @param exitCode 进程退出码（约定：0=成功，负数=异常场景）
     * @param output   捕获的输出行（保留最新MAX_OUTPUT_LINES行）
     * @param error    执行过程中发生的异常
     */
    public record CommandResult(int exitCode, List<String> output, Throwable error) {
        // 防御性拷贝（确保不可变性）
        public CommandResult {
            output = List.copyOf(output);
        }

        public boolean isSuccess() {
            return exitCode == 0 && error == null;
        }

        /**
         * 获取合并后的输出字符串（性能考虑，避免频繁调用）
         */
        public String getOutputStr() {
            return String.join(System.lineSeparator(), output);
        }

        /**
         * 获取格式化错误信息（包含堆栈顶层信息）
         */
        public String getErrorTrace() {
            if (error == null) {
                return "";
            }
            return String.format("[%s] %s%nCaused by: %s",
                    error.getClass().getSimpleName(),
                    error.getMessage(),
                    error.getCause());
        }
    }

}